Add scheduled loops for recurring prompt execution#52
Closed
zmanian wants to merge 28 commits intomcintyre94:mainfrom
Closed
Add scheduled loops for recurring prompt execution#52zmanian wants to merge 28 commits intomcintyre94:mainfrom
zmanian wants to merge 28 commits intomcintyre94:mainfrom
Conversation
97a9f83 to
8a9c9ce
Compare
Owner
|
This is a fun idea, I had the same thought about running cron type tasks on Sprites after seeing Openclaw and Claude Code's implementation. I do think cron and sprites is a really interesting design space, just not sure this app is the right place to actually kick off the tasks. My hesitancy is that iOS background tasks seems like a really unstable foundation for cron jobs, how reliable are you finding the triggers in practice? I really hope the Fly team might add native cron and then this app could just use that API to view/manage them. |
Provides permission requesting, notification content building, posting, and text truncation for loop completion summaries. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds LoopState, LoopInterval, LoopDuration, IterationStatus enums and SpriteLoop @model class with JSON-encoded iterations. Includes tests for defaults, presets, expiration logic, and time remaining display. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Create the Loop creation sheet with sprite name, prompt, interval picker, and duration picker. Add onLongPressSend callback to ChatInputBar with long-press gesture on the send button for triggering loop creation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements the core LoopManager service with register, pause, resume, stop, stopAll, and restoreLoops methods. Uses Timer-based scheduling with a running iterations guard to prevent overlapping executions. executeLoopPrompt is stubbed for wiring in Task 5. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the stub implementation with working code that: - Wakes the sprite if not running (poll up to 60s) - Builds a claude -p command with OAuth token - Executes via streamService and parses NDJSON output - Extracts assistant text blocks from Claude stream events - Cleans up the service after completion Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add LoopRowView with status indicator, prompt, sprite name, interval, and time remaining. Add LoopDetailView with configuration section, pause/resume/delete actions, and expandable iteration history. Add Loops section to DashboardView with swipe actions and context menus for pause/resume/delete. Also add missing files (LoopManager, NotificationService, CreateLoopSheet, and new views) to Xcode project. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add UIBackgroundModes (fetch) and BGTaskSchedulerPermittedIdentifiers to both Debug and Release build configurations. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Migrate from GENERATE_INFOPLIST_FILE to a custom Wisp/Info.plist to properly declare BGTaskSchedulerPermittedIdentifiers as an array (build settings only support a single string value). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Long-pressing the send button now presents the CreateLoopSheet, which creates a SpriteLoop, persists it to SwiftData, and registers it with the LoopManager to begin immediate execution. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…mage SwiftUI Button consumes touches, preventing onLongPressGesture from firing. Use separate onTapGesture and onLongPressGesture on an Image instead. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The handler closure was dispatched on an arbitrary background queue
(using: nil), causing a MainActor isolation assertion when constructing
@MainActor-isolated types (LoopManager, SpritesAPIClient) inside
Task { @mainactor in }. Using .main ensures the handler runs on the
main queue, matching the MainActor isolation requirements.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Documents that handleBackgroundRefresh must be called from MainActor context, matching the BGTaskScheduler handler's using: .main dispatch. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fails fast with a clear message if this is ever called off the main queue, rather than hitting an opaque Swift concurrency isolation crash. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Change keychain accessibility from WhenUnlockedThisDeviceOnly to AfterFirstUnlockThisDeviceOnly so background tasks can read API tokens when the device is locked. Migrate existing items on launch. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The runExec WebSocket call would fail immediately for cold/suspended sprites, then the code would proceed to streamService on a sprite that wasn't running yet. Now fire exec without awaiting, poll for up to 90s, and fail explicitly if the sprite doesn't reach running. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When the sprite just woke up, the first streamService call may fail with "network connection lost" before any data is received. Retry up to 3 times with 5s backoff for transient connection failures. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
503 errors mean the sprite isn't ready for service requests yet. Increase backoff to 10/20/30s, bump max attempts to 5, and re-verify sprite status before each retry attempt. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove the broken exec-based wake step from loops — the WebSocket exec doesn't actually trigger wake for cold sprites. Instead, let the streamService PUT (REST) trigger the wake; the existing retry loop handles 503s during startup. Also add retry logic to question tool install (up to 3 attempts with 5s backoff) since the sprite may still be waking when the first chat message is sent. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
If the question tool fails to install (e.g. sprite still waking), proceed with chat without the WispAsk MCP tool instead of showing a fatal error that blocks all messages. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The flush path after stream completion only handled assistant events, missing the result event where Claude's final response text lives. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The service log API sends stdout data as plain text strings, not base64-encoded bytes. Data(base64Encoded:) was silently returning nil for every chunk, causing all responses to be empty. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
bfcc34b to
703c4f7
Compare
Contributor
Author
|
Closing — scheduled actions feature has been removed from zaki-main. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Client-side recurring prompt execution ("loops") for babysitting PRs and collaborator interactions. Long-press the send button to create a loop with configurable intervals (5m-1h) and durations (1d-1mo). Loops appear in a new dashboard section with pause/resume/delete controls and full iteration history.
How it works
claude -pcall (no session resumption). Sprite is woken automatically if cold.Other changes
Wisp/Info.plistfor properBGTaskSchedulerPermittedIdentifiersarray supportNew files
Wisp/Models/Local/LoopModels.swift-- SwiftData models (SpriteLoop, LoopIteration, enums)Wisp/Services/LoopManager.swift-- Timer-based foreground + BGTask background executionWisp/Services/NotificationService.swift-- Local notification helpersWisp/Views/Loop/CreateLoopSheet.swift-- Loop creation formWisp/Views/Loop/LoopRowView.swift-- Dashboard row componentWisp/Views/Loop/LoopDetailView.swift-- Loop detail with iteration historyWisp/Info.plist-- Custom Info.plist with background modesWispTests/LoopModelTests.swift-- Model testsWispTests/LoopManagerTests.swift-- Manager testsWispTests/NotificationServiceTests.swift-- Notification testsTest plan
Generated with Claude Code